/*********************************************
 * vim:sw=8:ts=8:si:et
 * To use the above modeline in vim you must have "set modeline" in your .vimrc
 *
 * Author: Guido Socher
 * Copyright: GPL V2
 * See http://www.gnu.org/licenses/gpl.html
 *
 * IP, Arp, UDP and TCP functions.
 *
 * The TCP implementation uses some size optimisations which are valid
 * only if all data can be sent in one single packet. This is however
 * not a big limitation for a microcontroller as you will anyhow use
 * small web-pages. The TCP stack is therefore a SDP-TCP stack (single data packet TCP).
 *
 * A big advantage of this stack is that it can actually handle really serious
 * traffic. You can have many (e.g 100) users accessing a web-page almost in parallel.
 *
 * Chip type           : atmega88/atmega168/atmega328 with enc28j60
 *********************************************/
#include <avr/io.h>
#include <avr/pgmspace.h>
#include "avr_compat.h"
#include "net.h"
#include "enc28j60.h"

static uint8_t wwwport = 80; // 80 is just a default value. Gets overwritten during init
static uint8_t macaddr[6];
static uint8_t ipaddr[4];
static int16_t info_hdr_len = 0;
static int16_t info_data_len = 0;
static uint8_t seqnum = 0xa; // my initial tcp sequence number

// The Ip checksum is calculated over the ip header only starting
// with the header length field and a total length of 20 bytes
// unitl ip.dst
// You must set the IP checksum field to zero before you start
// the calculation.
// len for ip is 20.
//
// For UDP/TCP we do not make up the required pseudo header. Instead we
// use the ip.src and ip.dst fields of the real packet:
// The udp checksum calculation starts with the ip.src field
// Ip.src=4bytes,Ip.dst=4 bytes,Udp header=8bytes + data length=16+len
// In other words the len here is 8 + length over which you actually
// want to calculate the checksum.
// You must set the checksum field to zero before you start
// the calculation.
// len for udp is: 8 + 8 + data length
// len for tcp is: 4+4 + 20 + option len + data length
//
// For more information on how this algorithm works see:
// http://www.netfor2.com/checksum.html
// http://www.msc.uky.edu/ken/cs471/notes/chap3.htm
// The RFC has also a C code example: http://www.faqs.org/rfcs/rfc1071.html
uint16_t checksum(uint8_t *buf, uint16_t len, uint8_t type)
{
	// type 0=ip
	//      1=udp
	//      2=tcp
	uint32_t sum = 0;

	//if(type==0){
	//        // do not add anything
	//}
	if (type == 1)
	{
		sum += IP_PROTO_UDP_V; // protocol udp
		// the length here is the length of udp (data+header len)
		// =length given to this function - (IP.scr+IP.dst length)
		sum += len - 8; // = real tcp len
	}
	if (type == 2)
	{
		sum += IP_PROTO_TCP_V;
		// the length here is the length of tcp (data+header len)
		// =length given to this function - (IP.scr+IP.dst length)
		sum += len - 8; // = real tcp len
	}
	// build the sum of 16bit words
	while (len > 1)
	{
		sum += 0xFFFF & (((uint32_t) *buf << 8) | *(buf + 1));
		buf += 2;
		len -= 2;
	}
	// if there is a byte left then add it (padded with zero)
	if (len)
	{
		sum += ((uint32_t) (0xFF & *buf)) << 8;
	}
	// now calculate the sum over the bytes in the sum
	// until the result is only 16bit long
	while (sum >> 16)
	{
		sum = (sum & 0xFFFF) + (sum >> 16);
	}
	// build 1's complement:
	return ((uint16_t) sum ^ 0xFFFF);
}

// you must call this function once before you use any of the other functions:
void init_ip_arp_udp_tcp(uint8_t *mymac, uint8_t *myip, uint8_t wwwp)
{
	uint8_t i = 0;
	wwwport = wwwp;
	while (i < 4)
	{
		ipaddr[i] = myip[i];
		i++;
	}
	i = 0;
	while (i < 6)
	{
		macaddr[i] = mymac[i];
		i++;
	}
}

uint8_t eth_type_is_arp_and_my_ip(uint8_t *buf, uint16_t len)
{
	uint8_t i = 0;
	//
	if (len < 41)
	{
		return (0);
	}
	if (buf[ETH_TYPE_H_P] != ETHTYPE_ARP_H_V || buf[ETH_TYPE_L_P]
			!= ETHTYPE_ARP_L_V)
	{
		return (0);
	}
	while (i < 4)
	{
		if (buf[ETH_ARP_DST_IP_P+i] != ipaddr[i])
		{
			return (0);
		}
		i++;
	}
	return (1);
}

uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf, uint16_t len)
{
	uint8_t i = 0;
	//eth+ip+udp header is 42
	if (len < 42)
	{
		return (0);
	}
	if (buf[ETH_TYPE_H_P] != ETHTYPE_IP_H_V || buf[ETH_TYPE_L_P]
			!= ETHTYPE_IP_L_V)
	{
		return (0);
	}
	if (buf[IP_HEADER_LEN_VER_P] != 0x45)
	{
		// must be IP V4 and 20 byte header
		return (0);
	}
	while (i < 4)
	{
		if (buf[IP_DST_P+i] != ipaddr[i])
		{
			return (0);
		}
		i++;
	}
	return (1);
}
// make a return eth header from a received eth packet
void make_eth(uint8_t *buf)
{
	uint8_t i = 0;
	//
	//copy the destination mac from the source and fill my mac into src
	while (i < 6)
	{
		buf[ETH_DST_MAC +i] = buf[ETH_SRC_MAC +i];
		buf[ETH_SRC_MAC +i] = macaddr[i];
		i++;
	}
}
void fill_ip_hdr_checksum(uint8_t *buf)
{
	uint16_t ck;
	// clear the 2 byte checksum
	buf[IP_CHECKSUM_P] = 0;
	buf[IP_CHECKSUM_P+1] = 0;
	buf[IP_FLAGS_P] = 0x40; // don't fragment
	buf[IP_FLAGS_P+1] = 0; // fragement offset
	buf[IP_TTL_P] = 64; // ttl
	// calculate the checksum:
	ck = checksum(&buf[IP_P], IP_HEADER_LEN,0);
	buf[IP_CHECKSUM_P] = ck >> 8;
	buf[IP_CHECKSUM_P+1] = ck & 0xff;
}

// make a return ip header from a received ip packet
void make_ip(uint8_t *buf)
{
	uint8_t i = 0;
	while (i < 4)
	{
		buf[IP_DST_P+i] = buf[IP_SRC_P+i];
		buf[IP_SRC_P+i] = ipaddr[i];
		i++;
	}
	fill_ip_hdr_checksum(buf);
}

// make a return tcp header from a received tcp packet
// rel_ack_num is how much we must step the seq number received from the
// other side. We do not send more than 255 bytes of text (=data) in the tcp packet.
// If mss=1 then mss is included in the options list
//
// After calling this function you can fill in the first data byte at TCP_OPTIONS_P+4
// If cp_seq=0 then an initial sequence number is used (should be use in synack)
// otherwise it is copied from the packet we received
void make_tcphead(uint8_t *buf, uint16_t rel_ack_num, uint8_t mss,
		uint8_t cp_seq)
{
	uint8_t i = 0;
	uint8_t tseq;
	while (i < 2)
	{
		buf[TCP_DST_PORT_H_P+i] = buf[TCP_SRC_PORT_H_P+i];
		buf[TCP_SRC_PORT_H_P+i] = 0; // clear source port
		i++;
	}
	// set source port  (http):
	buf[TCP_SRC_PORT_L_P] = wwwport;
	i = 4;
	// sequence numbers:
	// add the rel ack num to SEQACK
	while (i > 0)
	{
		rel_ack_num = buf[TCP_SEQ_H_P+i - 1] + rel_ack_num;
		tseq = buf[TCP_SEQACK_H_P+i - 1];
		buf[TCP_SEQACK_H_P+i - 1] = 0xff & rel_ack_num;
		if (cp_seq)
		{
			// copy the acknum sent to us into the sequence number
			buf[TCP_SEQ_H_P+i - 1] = tseq;
		}
		else
		{
			buf[TCP_SEQ_H_P+i - 1] = 0; // some preset vallue
		}
		rel_ack_num = rel_ack_num >> 8;
		i--;
	}
	if (cp_seq == 0)
	{
		// put inital seq number
		buf[TCP_SEQ_H_P+0] = 0;
		buf[TCP_SEQ_H_P+1] = 0;
		// we step only the second byte, this allows us to send packts
		// with 255 bytes or 512 (if we step the initial seqnum by 2)
		buf[TCP_SEQ_H_P+2] = seqnum;
		buf[TCP_SEQ_H_P+3] = 0;
		// step the inititial seq num by something we will not use
		// during this tcp session:
		seqnum += 2;
	}
	// zero the checksum
	buf[TCP_CHECKSUM_H_P] = 0;
	buf[TCP_CHECKSUM_L_P] = 0;

	// The tcp header length is only a 4 bit field (the upper 4 bits).
	// It is calculated in units of 4 bytes.
	// E.g 24 bytes: 24/4=6 => 0x60=header len field
	//buf[TCP_HEADER_LEN_P]=(((TCP_HEADER_LEN_PLAIN+4)/4)) <<4; // 0x60
	if (mss)
	{
		// the only option we set is MSS to 1408:
		// 1408 in hex is 0x580
		buf[TCP_OPTIONS_P] = 2;
		buf[TCP_OPTIONS_P+1] = 4;
		buf[TCP_OPTIONS_P+2] = 0x05;
		buf[TCP_OPTIONS_P+3] = 0x80;
		// 24 bytes:
		buf[TCP_HEADER_LEN_P] = 0x60;
	}
	else
	{
		// no options:
		// 20 bytes:
		buf[TCP_HEADER_LEN_P] = 0x50;
	}
}

void make_arp_answer_from_request(uint8_t *buf)
{
	uint8_t i = 0;
	//
	make_eth(buf);
	buf[ETH_ARP_OPCODE_H_P] = ETH_ARP_OPCODE_REPLY_H_V;
	buf[ETH_ARP_OPCODE_L_P] = ETH_ARP_OPCODE_REPLY_L_V;
	// fill the mac addresses:
	while (i < 6)
	{
		buf[ETH_ARP_DST_MAC_P+i] = buf[ETH_ARP_SRC_MAC_P+i];
		buf[ETH_ARP_SRC_MAC_P+i] = macaddr[i];
		i++;
	}
	i = 0;
	while (i < 4)
	{
		buf[ETH_ARP_DST_IP_P+i] = buf[ETH_ARP_SRC_IP_P+i];
		buf[ETH_ARP_SRC_IP_P+i] = ipaddr[i];
		i++;
	}
	// eth+arp is 42 bytes:
	ENC28J60PacketSend(42, buf);
}

void make_echo_reply_from_request(uint8_t *buf, uint16_t len)
{
	make_eth(buf);
	make_ip(buf);
	buf[ICMP_TYPE_P] = ICMP_TYPE_ECHOREPLY_V;
	// we changed only the icmp.type field from request(=8) to reply(=0).
	// we can therefore easily correct the checksum:
	if (buf[ICMP_CHECKSUM_P] > (0xff - 0x08))
	{
		buf[ICMP_CHECKSUM_P+1]++;
	}
	buf[ICMP_CHECKSUM_P] += 0x08;
	//
	ENC28J60PacketSend(len, buf);
}

// you can send a max of 220 bytes of data
void make_udp_reply_from_request(uint8_t *buf, char *data, uint8_t datalen,
		uint16_t port)
{
	uint8_t i = 0;
	uint16_t ck;
	make_eth(buf);
	if (datalen > 220)
	{
		datalen = 220;
	}
	// total length field in the IP header must be set:
	buf[IP_TOTLEN_H_P] = 0;
	buf[IP_TOTLEN_L_P] = IP_HEADER_LEN+UDP_HEADER_LEN + datalen;
	make_ip(buf);
	// send to port:
	//buf[UDP_DST_PORT_H_P]=port>>8;
	//buf[UDP_DST_PORT_L_P]=port & 0xff;
	// sent to port of sender and use "port" as own source:
	buf[UDP_DST_PORT_H_P] = buf[UDP_SRC_PORT_H_P];
	buf[UDP_DST_PORT_L_P] = buf[UDP_SRC_PORT_L_P];
	buf[UDP_SRC_PORT_H_P] = port >> 8;
	buf[UDP_SRC_PORT_L_P] = port & 0xff;
	// calculte the udp length:
	buf[UDP_LEN_H_P] = 0;
	buf[UDP_LEN_L_P] = UDP_HEADER_LEN+datalen;
	// zero the checksum
	buf[UDP_CHECKSUM_H_P] = 0;
	buf[UDP_CHECKSUM_L_P] = 0;
	// copy the data:
	while (i < datalen)
	{
		buf[UDP_DATA_P+i] = data[i];
		i++;
	}
	ck = checksum(&buf[IP_SRC_P], 16 + datalen, 1);
	buf[UDP_CHECKSUM_H_P] = ck >> 8;
	buf[UDP_CHECKSUM_L_P] = ck & 0xff;
	ENC28J60PacketSend(UDP_HEADER_LEN+IP_HEADER_LEN + ETH_HEADER_LEN + datalen,
			buf);
}

void make_tcp_synack_from_syn(uint8_t *buf)
{
	uint16_t ck;
	make_eth(buf);
	// total length field in the IP header must be set:
	// 20 bytes IP + 24 bytes (20tcp+4tcp options)
	buf[IP_TOTLEN_H_P] = 0;
	buf[IP_TOTLEN_L_P] = IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN + 4;
	make_ip(buf);
	buf[TCP_FLAGS_P] = TCP_FLAGS_SYNACK_V;
	make_tcphead(buf, 1, 1, 0);
	// calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + 4 (one option: mss)
	ck = checksum(&buf[IP_SRC_P], 8 + TCP_HEADER_LEN_PLAIN + 4, 2);
	buf[TCP_CHECKSUM_H_P] = ck >> 8;
	buf[TCP_CHECKSUM_L_P] = ck & 0xff;
	// add 4 for option mss:
	ENC28J60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN + 4 + ETH_HEADER_LEN,
			buf);
}

// get a pointer to the start of tcp data in buf
// Returns 0 if there is no data
// You must call init_len_info once before calling this function
uint16_t get_tcp_data_pointer(void)
{
	if (info_data_len)
	{
		return ((uint16_t) TCP_SRC_PORT_H_P + info_hdr_len);
	}
	else
	{
		return (0);
	}
}

// do some basic length calculations and store the result in static varibales
void init_len_info(uint8_t *buf)
{
	info_data_len = (((int16_t) buf[IP_TOTLEN_H_P]) << 8) | (buf[IP_TOTLEN_L_P]
			& 0xff);
	info_data_len -= IP_HEADER_LEN;
	info_hdr_len = (buf[TCP_HEADER_LEN_P] >> 4) * 4; // generate len in bytes;
	info_data_len -= info_hdr_len;
	if (info_data_len <= 0)
	{
		info_data_len = 0;
	}
}

// fill in tcp data at position pos. pos=0 means start of
// tcp data. Returns the position at which the string after
// this string could be filled.
uint16_t fill_tcp_data_p(uint8_t *buf, uint16_t pos, const prog_char *progmem_s)
{
	char c;
	// fill in tcp data at position pos
	//
	// with no options the data starts after the checksum + 2 more bytes (urgent ptr)
	while ((c = pgm_read_byte(progmem_s++)))
	{
		buf[TCP_CHECKSUM_L_P+3 + pos] = c;
		pos++;
	}
	return (pos);
}

// fill in tcp data at position pos. pos=0 means start of
// tcp data. Returns the position at which the string after
// this string could be filled.
uint16_t fill_tcp_data(uint8_t *buf, uint16_t pos, const char *s)
{
	// fill in tcp data at position pos
	//
	// with no options the data starts after the checksum + 2 more bytes (urgent ptr)
	while (*s)
	{
		buf[TCP_CHECKSUM_L_P+3 + pos] = *s;
		pos++;
		s++;
	}
	return (pos);
}

// Make just an ack packet with no tcp data inside
// This will modify the eth/ip/tcp header
void make_tcp_ack_from_any(uint8_t *buf)
{
	uint16_t j;
	make_eth(buf);
	// fill the header:
	buf[TCP_FLAGS_P] = TCP_FLAGS_ACK_V;
	if (info_data_len == 0)
	{
		// if there is no data then we must still acknoledge one packet
		make_tcphead(buf, 1, 0, 1); // no options
	}
	else
	{
		make_tcphead(buf, info_data_len, 0, 1); // no options
	}

	// total length field in the IP header must be set:
	// 20 bytes IP + 20 bytes tcp (when no options)
	j = IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN;
	buf[IP_TOTLEN_H_P] = j >> 8;
	buf[IP_TOTLEN_L_P] = j & 0xff;
	make_ip(buf);
	// calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len
	j = checksum(&buf[IP_SRC_P], 8 + TCP_HEADER_LEN_PLAIN, 2);
	buf[TCP_CHECKSUM_H_P] = j >> 8;
	buf[TCP_CHECKSUM_L_P] = j & 0xff;
	ENC28J60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN + ETH_HEADER_LEN, buf);
}

// you must have called init_len_info at some time before calling this function
// dlen is the amount of tcp data (http data) we send in this packet
// You can use this function only immediately after make_tcp_ack_from_any
// This is because this function will NOT modify the eth/ip/tcp header except for
// length and checksum
void make_tcp_ack_with_data(uint8_t *buf, uint16_t dlen)
{
	uint16_t j;
	// fill the header:
	// This code requires that we send only one data packet
	// because we keep no state information. We must therefore set
	// the fin here:
	buf[TCP_FLAGS_P] = TCP_FLAGS_ACK_V|TCP_FLAGS_PUSH_V | TCP_FLAGS_FIN_V;

	// total length field in the IP header must be set:
	// 20 bytes IP + 20 bytes tcp (when no options) + len of data
	j = IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN + dlen;
	buf[IP_TOTLEN_H_P] = j >> 8;
	buf[IP_TOTLEN_L_P] = j & 0xff;
	fill_ip_hdr_checksum(buf);
	// zero the checksum
	buf[TCP_CHECKSUM_H_P] = 0;
	buf[TCP_CHECKSUM_L_P] = 0;
	// calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len
	j = checksum(&buf[IP_SRC_P], 8 + TCP_HEADER_LEN_PLAIN + dlen, 2);
	buf[TCP_CHECKSUM_H_P] = j >> 8;
	buf[TCP_CHECKSUM_L_P] = j & 0xff;
	ENC28J60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN + dlen
			+ ETH_HEADER_LEN, buf);
}

/* end of ip_arp_udp.c */
